# -*- coding: utf-8 -*-
#
# Author:: Jonny
# Date:: 2020/8/18
'''

'''

import json
import re
import os
from pathlib import Path

from submodules.common.lib.log_ import logger



def json_to_dict(json_str):
    return json.loads(json_str)

def json_file_to_dict(file_path):
    with open(file_path, 'r', encoding='utf8') as fp:
        json_data = json.load(fp)
    return json_data


def json_files_to_dict(file_path_list):
    res_dict = {}
    for file_path in file_path_list:
        p = Path(file_path)
        p = p.with_suffix("")
        with open(file_path, 'r', encoding='utf8') as fp:
            json_data = json.load(fp)
        res_dict.update({f"{p.name}": json_data})
    return res_dict


def json_dir_to_dict(dir_path):
    if os.path.isdir(dir_path):
        file_path_list = [os.path.join(dir_path, x) for x in os.listdir(dir_path)]
        file_path_list = [x for x in file_path_list if x.endswith(".json")]
        return json_files_to_dict(file_path_list)
    else:
        logger.error(f"dir_path:{dir_path} is not a dir")
        return None


def dict_to_json_file(d1, file_path):
    logger.info("file_path:%s" % file_path)
    # with open(os.path.join(project_path, file_path), 'w', encoding='utf8')as fp:
    with open(file_path, 'w', encoding='utf8') as fp:
        fp.write(json.dumps(d1, ensure_ascii=False))
    fp.close()
    return True


def _is_json_str(_josn_str):
    try:
        d = json.loads(_josn_str)
        return d
    except:
        return False


def _is_escaped_json_str(_josn_str):
    try:
        if "{" in _josn_str and "}" in _josn_str and '\"' in _josn_str:
            try:
                d = json.loads(_josn_str.replace('\"', '"'))
                return d
            except:
                return False
    except:
        return False


def escaped_json_str_to_dict(_josn_str):
    res = _is_escaped_json_str(_josn_str)
    return res


def _is_iterable(_data, mode=""):
    '''
    非空数组、非空对象、可遍历的json_str
    '''
    if isinstance(_data, list) and len(_data) > 0:
        return True
    if isinstance(_data, dict) and len(_data) > 0:
        return True
    if mode == "ext":
        if _is_escaped_json_str(_data):
            return True


def _get_value(_data, key_or_index):
    try:
        return _data[key_or_index]
    except:
        return


def __is_startswith(str, l):
    for item in l:
        if str.startswith(item):
            return True
    return False


def __is_match(str, l):
    for pattern in l:
        # if len(re.match(pattern, str)) > 0:
        if re.search(pattern, str) is not None:
            return True
    return False


def diff_json(j1, j2, p="", res=None, mode="", benchmark="left", diff_type="value", exclude_key_name_list=[],
              exclude_path_list=[],
              exclude_prefix_list=[], oct_key_name_list=[], exclude_re_pattern_list=[]):
    '''
    mod="ext"
        遇到可value为json字符串时继续继续尝试解析并对比
    diff_type="value"
        对比json各节点的值
    diff_type="data_type"
        对比json各节点的数据类型

    oct_key_name_list:
        oct means only check type，只有在diff_type=value时起作用。
    输出一个list
    list中的每一个元素包含JSONPath-path、j1对应JSONPath的内容-v1、j2对应JSONPath的内容-v2
    '''
    if res is None:
        res = []
    j1_type = type(j1)
    j2_type = type(j2)
    if j1_type != j2_type:
        logger.info('根节点数据类型不一致！')
    else:
        if j1_type == dict:
            if benchmark == "left":
                _d = j1
            elif benchmark == "right":
                _d = j2
            elif benchmark == "merge":
                _d = dict(j1, **j2)
            p_tmp = p
            for k in _d:
                p = p_tmp
                j1_v = _get_value(j1, k)
                j2_v = _get_value(j2, k)
                if _is_iterable(j1_v) and _is_iterable(j2_v):
                    p = p + '.' + str(k)
                    diff_json(j1_v, j2_v, p, res, mode=mode, benchmark=benchmark,
                              exclude_key_name_list=exclude_key_name_list,
                              exclude_path_list=exclude_path_list, exclude_prefix_list=exclude_prefix_list,
                              diff_type=diff_type, oct_key_name_list=oct_key_name_list,
                              exclude_re_pattern_list=exclude_re_pattern_list)
                elif mode == "ext" and _is_escaped_json_str(j1_v) and _is_escaped_json_str(j2_v):
                    p = p + '.' + str(k)
                    j1_v = escaped_json_str_to_dict(j1_v)
                    j2_v = escaped_json_str_to_dict(j2_v)
                    diff_json(j1_v, j2_v, p, res, mode=mode, benchmark=benchmark,
                              exclude_key_name_list=exclude_key_name_list,
                              exclude_path_list=exclude_path_list, exclude_prefix_list=exclude_prefix_list,
                              diff_type=diff_type, oct_key_name_list=oct_key_name_list,
                              exclude_re_pattern_list=exclude_re_pattern_list)
                else:
                    p_final = p + '.' + str(k)
                    p_final_full = f"${p_final}"
                    if k not in exclude_key_name_list and p_final_full not in exclude_path_list and not __is_startswith(
                            p_final_full, exclude_prefix_list) and not __is_match(
                        p_final_full, exclude_re_pattern_list):
                        # 排除不需要对比的情况
                        if diff_type == "value":
                            if j1_v != j2_v:
                                if k in oct_key_name_list:
                                    j1_v_type = type(j1_v)
                                    j2_v_type = type(j2_v)
                                    if j1_v_type != j2_v_type:
                                        res.append(
                                            {'path': p_final_full, 'v1': j1_v, 'v2': j2_v,
                                             'v1_type': f"{j1_v_type}",
                                             'v2_type': f"{j2_v_type}"})
                                else:
                                    res.append(
                                        {'path': p_final_full, 'v1': j1_v, 'v2': j2_v})
                        elif diff_type == "type":
                            j1_v_type = type(j1_v)
                            j2_v_type = type(j2_v)
                            if j1_v_type != j2_v_type:
                                res.append(
                                    {'path': p_final_full, 'v1': j1_v, 'v2': j2_v, 'v1_type': f"{j1_v_type}",
                                     'v2_type': f"{j2_v_type}"})
        elif j1_type == list:
            if benchmark == "left":
                l = len(j1)
            elif benchmark == "right":
                l = len(j2)
            elif benchmark == "merged":
                l1 = len(j1)
                l2 = len(j2)
                if l1 > l2:
                    l = l1
                else:
                    l = l2
            for i in range(l):
                if i == 0:
                    p = p + '[' + str(i) + ']'
                else:
                    p = p[:-path_lenght] + '[' + str(i) + ']'
                path_lenght = len(str(i)) + 2
                l1_v = _get_value(j1, i)
                l2_v = _get_value(j2, i)
                if _is_iterable(l1_v) and _is_iterable(l2_v):
                    diff_json(l1_v, l2_v, p, res, mode=mode, benchmark=benchmark,
                              exclude_key_name_list=exclude_key_name_list,
                              exclude_prefix_list=exclude_prefix_list,
                              exclude_path_list=exclude_path_list, diff_type=diff_type,
                              oct_key_name_list=oct_key_name_list, exclude_re_pattern_list=exclude_re_pattern_list)
                elif mode == "ext" and _is_escaped_json_str(l1_v) and _is_escaped_json_str(l2_v):
                    l1_v = escaped_json_str_to_dict(l1_v)
                    l2_v = escaped_json_str_to_dict(l2_v)
                    diff_json(l1_v, l2_v, p, res, mode=mode, benchmark=benchmark,
                              exclude_key_name_list=exclude_key_name_list,
                              exclude_prefix_list=exclude_prefix_list,
                              exclude_path_list=exclude_path_list, diff_type=diff_type,
                              oct_key_name_list=oct_key_name_list, exclude_re_pattern_list=exclude_re_pattern_list)
                else:
                    k = p.split(".")[-1].split("[")[0]
                    p_final_full = f"${p}"
                    if k not in exclude_key_name_list and p_final_full not in exclude_path_list and not __is_startswith(
                            p_final_full, exclude_prefix_list) and not __is_match(
                        p_final_full, exclude_re_pattern_list):
                        if diff_type == "value":
                            if l1_v != l2_v:
                                if k in oct_key_name_list:
                                    l1_v_type = type(l1_v)
                                    l2_v_type = type(l2_v)
                                    if l1_v_type != l2_v_type:
                                        res.append(
                                            {'path': p_final_full, 'v1': l1_v, 'v2': l2_v, 'v1_type': f"{l1_v_type}",
                                             'v2_type': f"{l2_v_type}"})
                                else:
                                    res.append(
                                        {'path': p_final_full, 'v1': l1_v, 'v2': l2_v})
                        elif diff_type == "type":
                            l1_v_type = type(l1_v)
                            l2_v_type = type(l2_v)
                            if l1_v_type != l2_v_type:
                                res.append({'path': p_final_full, 'v1': l1_v, 'v2': l2_v, 'v1_type': f"{l1_v_type}",
                                            'v2_type': f"{l2_v_type}"})
    return res


def loads_jsonp(jsonp_str):
    """
    解析jsonp数据格式为json
    :return:
    """
    try:
        return json.loads(re.match(".*?({.*}).*", jsonp_str, re.S).group(1))
    except:
        raise ValueError('Invalid Input')


if __name__ == "__main__":
    import os
    from submodules.common.lib.local_paths import project_path

    test_data_dir = os.path.join(project_path, "submodules", "common", "test_data")

    json_str1 = ""
    json_str2 = ""

    d1 = {
        # "aaa": "1111",
        # "a2": [{"a21": "21"}, {"a22": "22"}, {"a23": "23"}],
        # "a3": [{"a21": "21"}],
        # "a3": ["a21"],
        "a3": {"aaa": "aaa"},
        "a4": {"aaa": {"bbb": "bbb"}},
        # "aaaa": "111",
        "aaaa": {"aaa": "ccc"},

        # "bbb": {
        #     "ccc": "333",
        #     "ddd": "333",
        #     "hhh": "667",
        #     'eee': {
        #         'fff': '777',
        #         'ggg': '888',
        #
        #     }
        # }
    }

    d2 = {
        # "aaa": "1111",
        # "a2": [{"a21": "22"}, {"a22": "22"}, {"a23": "23"}],
        "a3": {"aaa": "aaa"},
        "a4": {"aaa": {"bbb": "bbb"}},

        # "bbb": {
        #     "ccc": "222",
        #     "ddd": "333",
        #     "hhh": "666",
        #     'eee': {
        #         'fff': '7777',
        #     }
        #
        # },
        # "eee": "555"
    }

    d3 = [1, 2, 3]
    d4 = [1]

    d5 = {"aaa": "111"}
    d6 = {"aaa": "222"}


    def test_diff_json():
        res = diff_json(d1, d2)

        print(res)


    def test_diff_json2():
        exclude_key_name_list = ["diff_code", "gid", "title", "schema", "impr_id", "group_id", "impr_id", "group_id"]
        json1_path = os.path.join(test_data_dir, "json_", "big_json1.json")
        json2_path = os.path.join(test_data_dir, "json_", "big_json2.json")

        json1 = json_file_to_dict(json1_path)
        json2 = json_file_to_dict(json2_path)

        # print(json1)
        # print(json2)

        res = diff_json(json1, json2, exclude_key_name_list=exclude_key_name_list)
        print(res)


    def test_diff_json3():
        d1 = {
            "aaa": [1, 2, 3]
        }

        d2 = {
            "aaa": [1, 4, 3]
        }
        exclude_path_list = ["$.aaa[1]"]
        res = diff_json(d1, d2, exclude_path_list=exclude_path_list)
        print(res)

        d1 = {
            "aaa": [1, 2, 3],
            "bbb": {
                "zzz": {
                    "xxx": [1, 2, 3],
                    "ccc": {
                        "vvv": "nnn",
                        "bbb": "mmm"
                    }
                }
            }
        }

        d2 = {
            "aaa": [1, 4, 3],
            "bbb": {
                "zzz": {
                    "xxx": [1, 7, 3],
                    "ccc": {
                        "vvv": "nnnn",
                        "bbb": "mmmm"
                    }
                }
            }
        }
        exclude_path_list = ["$.aaa[1]", "$.bbb.zzz.xxx[1]", "$.bbb.zzz.ccc.bbb"]
        exclude_key_name_list = ["vvv"]
        res = diff_json(d1, d2, exclude_key_name_list=exclude_key_name_list, exclude_path_list=exclude_path_list)
        print(res)


    def test_diff_json4():
        d1 = {
            "aaa": [1, 2, 3]
        }

        d2 = {
            "aaa": [1, 2, 4]
        }
        res = diff_json(d1, d2)
        print(res)
        res = diff_json(d1, d2, diff_type="type")
        print(res)
        d1 = {
            "aaa": [1, 2, 3]
        }

        d2 = {
            "aaa": "[1, 4, 3]"
        }
        res = diff_json(d1, d2, diff_type="type")
        print(res)


    def test_diff_json5():
        '''
        测试startwith功能点
        '''
        d1 = {
            "aaa": [1, 2, 3]
        }

        d2 = {
            "aaa": [1, 4, 3]
        }
        exclude_path_list = ["$.aaa[1]"]
        res = diff_json(d1, d2, exclude_path_list=exclude_path_list)
        print(res)

        d1 = {
            "aaa": [1, 2, 3],
            "bbb": {
                "zzz": {
                    "xxx": [1, 2, 3],
                    "ccc": {
                        "vvv": "nnn",
                        "bbb": "mmm"
                    }
                }
            }
        }

        d2 = {
            "aaa": [1, 4, 3],
            "bbb": {
                "zzz": {
                    "xxx": [1, 7, 3],
                    "ccc": {
                        "vvv": "nnnn",
                        "bbb": "mmmm"
                    }
                }
            }
        }
        exclude_path_list = ["$.aaa[1]"]
        exclude_key_name_list = ["vvv"]
        exclude_prefix_list = ["$.bbb.z"]
        # exclude_prefix_list = []
        res = diff_json(d1, d2, exclude_key_name_list=exclude_key_name_list, exclude_path_list=exclude_path_list,
                        exclude_prefix_list=exclude_prefix_list)
        print(res)


    def test_diff_json6():
        '''
        测试startwith功能点
        '''
        d1 = {
            "aaa": [1, 2, 3]
        }

        d2 = {
            "aaa": [1, 4, 3]
        }
        exclude_path_list = ["$.aaa[1]"]
        res = diff_json(d1, d2, exclude_path_list=exclude_path_list)
        print(res)

        d1 = {
            "aaa": [1, 2, 3],
            "bbb": {
                "zzz": {
                    "xxx": [1, 2, 3],
                    "ccc": {
                        "vvv": "nnn",
                        "bbb": "mmm"
                    }
                }
            }
        }

        d2 = {
            "aaa": [1, 4, 3],
            "bbb": {
                "zzz": {
                    "xxx": [1, 7, 3],
                    "ccc": {
                        "vvv": "nnnn",
                        "bbb": "mmmm"
                    }
                }
            }
        }
        exclude_path_list = ["$.aaa[1]"]
        exclude_key_name_list = ["vvv"]
        exclude_prefix_list = ["$.bbb.z"]
        exclude_prefix_list = []
        oct_key_name_list = ["bbb", "xxx"]
        oct_key_name_list = []
        res = diff_json(d1, d2, exclude_key_name_list=exclude_key_name_list, exclude_path_list=exclude_path_list,
                        exclude_prefix_list=exclude_prefix_list, oct_key_name_list=oct_key_name_list)
        print(res)


    def test_diff_json7():
        '''
        测试startwith功能点
        '''
        d1 = {
            "aaa": [1, 2, 3]
        }

        d2 = {
            "aaa": [1, 4, 3]
        }
        exclude_path_list = ["$.aaa[1]"]
        res = diff_json(d1, d2, exclude_path_list=exclude_path_list)
        print(res)

        d1 = {
            "aaa": [1, 2, 3],
            "bbb": {
                "zzz": {
                    "xxx": [1, 2, 3],
                    "ccc": {
                        "vvv": "nnn",
                        "bbb": "mmm"
                    }
                }
            }
        }

        d2 = {
            "aaa": [1, 4, 3],
            "bbb": {
                "zzz": {
                    "xxx": [1, 7, 3],
                    "ccc": {
                        "vvv": "nnnn",
                        "bbb": "mmmm"
                    }
                }
            }
        }
        exclude_path_list = ["$.aaa[1]"]
        exclude_key_name_list = ["vvv"]
        exclude_prefix_list = ["$.bbb.z"]
        exclude_prefix_list = []
        # oct_key_name_list = ["bbb", "xxx"]
        oct_key_name_list = []
        # exclude_re_pattern_list = ["\$.bbb.zzz.xxx\[[1-999]\]", "\$.bbb.zzz.ccc.bbb"]
        exclude_re_pattern_list = []
        res = diff_json(d1, d2, exclude_key_name_list=exclude_key_name_list, exclude_path_list=exclude_path_list,
                        exclude_prefix_list=exclude_prefix_list, oct_key_name_list=oct_key_name_list,
                        exclude_re_pattern_list=exclude_re_pattern_list)
        print(res)


    def test_json_file_to_dict():
        import os
        from submodules.common.lib import local_paths
        data_path = os.path.join(local_paths.project_path, "submodules", "common", "test_data", "valid.json")
        # data_path = os.path.join(local_paths.project_path, "submodules", "common", "test_data", "invalid.json")
        print(data_path)
        res = json_file_to_dict(data_path)
        print(res)


    test_diff_json6()
    test_diff_json7()

    s = '''$.data.recommended_realtors[1].realtor_tags[2]'''
    pattern_list = [
        r"\$.data.recommended_realtors\[\d{1,3}\].realtor_tags",
    ]

    print(__is_match(s, pattern_list))
